<!DOCTYPE html>
<html lang="en">
<head>
  <title>Quick Start - Born to build better enterprise frameworks and apps</title>
  <meta charset="utf-8">
  <meta name="description" content="index.description">
  <meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
  <link rel="icon" href="/images/favicon.png" type="image/x-icon">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/docsearch.js/2/docsearch.min.css" />
<link rel="stylesheet" href="/css/index.css">

    <script>
    !function(t,e,a,r,c){t.TracertCmdCache=t.TracertCmdCache||[],t[c]=window[c]||
      {_isInit:!0,call:function(){t.TracertCmdCache.push(arguments)},
      start:function(t){this.call('start',t)}},t[c].l=new Date;
      var n=e.createElement(a),s=e.getElementsByTagName(a)[0];
      n.async=!0,n.src=r,s.parentNode.insertBefore(n,s)}
    (window,document,'script','https://tracert.alipay.com/tracert.js','Tracert');
      Tracert.start({
        plugins: [ 'BucName' ],
        spmAPos: 'a454',
        spmBPos: 'b4893',
      });
    </script>
  
</head>
<body>
  <div class="nav" >
  <header>
    <a href="/en/" class="nav-logo leftpadding" alt="egg"><img src="https://zos.alipayobjects.com/rmsportal/VTcUYAaoKqXyHJbLAPyF.svg"></a>
    <ul class="nav-item">
      <li>
        <form id="search-form">
          <input type="text" id="search-query" class="search-query st-default-search-input">
        </form>
      </li>
      <li><a href="/en/intro/" alt="Guide">Guide</a></li><li><a href="/api/" alt="API">API</a></li><li><a href="/en/tutorials/index.html" alt="Tutorials">Tutorials</a></li><li><a href="https://github.com/search?q=topic%3Aegg-plugin&type=Repositories" alt="Plugins">Plugins</a></li><li><a href="https://github.com/eggjs/egg/releases" alt="Release">Release</a></li>
      
      
        <li class="translations">
          <a class="nav-link">中文文档</a>
          <span class="arrow"></span><ul id="dropdownContent" class="dropdown-content"><li><a id="en" href="/en/intro/quickstart.html" style="color: #22ab28">English</a></li><li><a id="zh-cn" href="/zh-cn/intro/quickstart.html" >中文</a></li></ul>
        </li>
      
      <li><iframe src="https://ghbtns.com/github-btn.html?user=eggjs&repo=egg&type=star&count=true" frameborder="0" scrolling="0" width="150px" height="20px"></iframe></li>
    </ul>
    <a id="mobileTrigger" href="#" class="mobile-trigger">
      <ul>
        <li></li>
        <li></li>
        <li></li>
      </ul>
    </a>
  </header>
</div>
  <div id="container" class="container">
    <div class="page-main">
  <article class="markdown-body">
    <h1>Quick Start</h1>
    <p>This guide covers getting up and running a real example using Egg.
By following along with this guide step by step, you can quickly get started with Egg development.</p>
<h2 id="prerequisites"><a class="markdown-anchor" href="#prerequisites">#</a> Prerequisites</h2>
<ul>
<li>Operating System: Linux, OS X or Windows.</li>
<li>Node.js Runtime: 8.x or newer; it is recommended that you use <a href="http://nodejs.org" target="_blank" rel="noopener">LTS Releases</a>.</li>
</ul>
<h2 id="the-quick-way"><a class="markdown-anchor" href="#the-quick-way">#</a> the Quick Way</h2>
<p>To begin with, let's quickly initialize the project by using a scaffold,
which will quickly generate some of the major pieces of the application.</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ npm i egg-init -g</span><br><span class="line">$ egg-init egg-example --<span class="built_in">type</span>=simple</span><br><span class="line">$ <span class="built_in">cd</span> egg-example</span><br><span class="line">$ npm i</span><br></pre></td></tr></table></figure>
<p>Then get up and run by using the following commands.</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ npm run dev</span><br><span class="line">$ open localhost:7001</span><br></pre></td></tr></table></figure>
<h2 id="step-by-step"><a class="markdown-anchor" href="#step-by-step">#</a> Step by Step</h2>
<p>Usually you can just use <a href="https://github.com/eggjs/egg-init" target="_blank" rel="noopener">egg-init</a> of the previous section,
choose a scaffold that best fits your business model and quickly generate a project,
then get started with the development.</p>
<p>However, in this section, instead of using scaffolds we will build a project called <a href="https://github.com/eggjs/examples/tree/master/hackernews" target="_blank" rel="noopener">Egg HackerNews</a> step by step, for a better understanding of how it works.</p>
<p><img src="https://cloud.githubusercontent.com/assets/227713/22960991/812999bc-f37d-11e6-8bd5-a96ca37d0ff2.png" alt="Egg HackerNews Snapshoot"></p>
<h3 id="initialization"><a class="markdown-anchor" href="#initialization">#</a> Initialization</h3>
<p>First let's create the project directory and initialize its structure.</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ mkdir egg-example</span><br><span class="line">$ <span class="built_in">cd</span> egg-example</span><br><span class="line">$ npm init</span><br><span class="line">$ npm i egg --save</span><br><span class="line">$ npm i egg-bin --save-dev</span><br></pre></td></tr></table></figure>
<p>Then add <code>npm scripts</code> to <code>package.json</code>.</p>
<figure class="highlight json"><table><tr><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="attr">"name"</span>: <span class="string">"egg-example"</span>,</span><br><span class="line">  <span class="attr">"scripts"</span>: &#123;</span><br><span class="line">    <span class="attr">"dev"</span>: <span class="string">"egg-bin dev"</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="create-a-controller"><a class="markdown-anchor" href="#create-a-controller">#</a> Create a Controller</h3>
<p>If you are familiar with the MVC architecture,
you might have already guessed that the first thing to create
is a <a href="../basics/controller.html">controller</a> and <a href="../basics/router.html">router</a>.</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// app/controller/home.js</span></span><br><span class="line"><span class="keyword">const</span> Controller = <span class="built_in">require</span>(<span class="string">'egg'</span>).Controller;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">HomeController</span> <span class="keyword">extends</span> <span class="title">Controller</span> </span>&#123;</span><br><span class="line">  <span class="keyword">async</span> index() &#123;</span><br><span class="line">    <span class="keyword">this</span>.ctx.body = <span class="string">'Hello world'</span>;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = HomeController;</span><br></pre></td></tr></table></figure>
<p>Then edit the router file and add a mapping.</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// app/router.js</span></span><br><span class="line"><span class="built_in">module</span>.exports = <span class="function"><span class="params">app</span> =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">const</span> &#123; router, controller &#125; = app;</span><br><span class="line">  router.get(<span class="string">'/'</span>, controller.home.index);</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<p>Then add a <a href="../basics/config.html">configuration</a> file:</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// config/config.default.js</span></span><br><span class="line">exports.keys = <span class="xml"><span class="tag">&lt;<span class="name">YOUR_SECURITY_COOKE_KEYS</span>&gt;</span>;</span></span><br></pre></td></tr></table></figure>
<p>The project directory looks like this:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">egg-example</span><br><span class="line">├── app</span><br><span class="line">│   ├── controller</span><br><span class="line">│   │   └── home.js</span><br><span class="line">│   └── router.js</span><br><span class="line">├── config</span><br><span class="line">│   └── config.default.js</span><br><span class="line">└── package.json</span><br></pre></td></tr></table></figure>
<p>For more information about directory structure, see <a href="../basics/structure.html">Directory Structure</a>.</p>
<p>Now you can start up the Web Server and see your application in action.</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ npm run dev</span><br><span class="line">$ open localhost:7001</span><br></pre></td></tr></table></figure>
<blockquote>
<p>Note：</p>
<ul>
<li>You could write <code>Controller</code> with <code>class</code> or <code>exports</code> style, see more detail at <a href="../basics/controller.html">Controller</a>.</li>
<li>And <code>Config</code> could write with <code>module.exports</code> or <code>exports</code> style, see more detail at <a href="https://nodejs.org/api/modules.html#modules_exports_shortcut" target="_blank" rel="noopener">Node.js modules docs</a>.</li>
</ul>
</blockquote>
<h3 id="add-static-assets"><a class="markdown-anchor" href="#add-static-assets">#</a> Add Static Assets</h3>
<p>Egg has a built-in plugin called <a href="https://github.com/eggjs/egg-static" target="_blank" rel="noopener">static</a>.
In production, it is recommended that you deploy static assets to CDN instead of using this plugin.</p>
<p><a href="https://github.com/eggjs/egg-static" target="_blank" rel="noopener">static</a> maps <code>/public/*</code> to the directory <code>app/public/*</code> by default.</p>
<p>In this case, we just need to put our static assets into the directory <code>app/public</code>.</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">app/public</span><br><span class="line">├── css</span><br><span class="line">│   └── news.css</span><br><span class="line">└── js</span><br><span class="line">    ├── lib.js</span><br><span class="line">    └── news.js</span><br></pre></td></tr></table></figure>
<h3 id="add-templates-for-rendering"><a class="markdown-anchor" href="#add-templates-for-rendering">#</a> Add Templates for Rendering</h3>
<p>In most cases, data are usually read, processed and rendered by the templates before being presented to the user.
Thus we need to introduce corresponding template engines to handle it.</p>
<p>Egg does not force to use any particular template engines,
but specifies the <a href="../advanced/view-plugin.html">View Plug-ins Specification</a>
to allow the developers to use different plug-ins for their individual needs instead.</p>
<p>For more information, cf. <a href="../core/view.html">View</a>.</p>
<p>In this example, we will use <a href="https://mozilla.github.io/nunjucks/" target="_blank" rel="noopener">Nunjucks</a>.</p>
<p>First install the corresponding plug-in <a href="https://github.com/eggjs/egg-view-nunjucks" target="_blank" rel="noopener">egg-view-nunjucks</a>.</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ npm i egg-view-nunjucks --save</span><br></pre></td></tr></table></figure>
<p>And enable it.</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// config/plugin.js</span></span><br><span class="line">exports.nunjucks = &#123;</span><br><span class="line">  enable: <span class="literal">true</span>,</span><br><span class="line">  package: <span class="string">'egg-view-nunjucks'</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// config/config.default.js</span></span><br><span class="line">exports.keys = <span class="xml"><span class="tag">&lt;<span class="name">YOUR_SECURITY_COOKE_KEYS</span>&gt;</span>;</span></span><br><span class="line"><span class="xml">// add view's configurations</span></span><br><span class="line"><span class="xml">exports.view = &#123;</span></span><br><span class="line"><span class="xml">  defaultViewEngine: 'nunjucks',</span></span><br><span class="line"><span class="xml">  mapping: &#123;</span></span><br><span class="line"><span class="xml">    '.tpl': 'nunjucks',</span></span><br><span class="line"><span class="xml">  &#125;,</span></span><br><span class="line"><span class="xml">&#125;;</span></span><br></pre></td></tr></table></figure>
<p><strong>Carefully! <code>config</code> dir, not <code>app/config</code>!</strong></p>
<p>Then create a template for the index page.
This usually goes to the app/view directory.</p>
<figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="comment">&lt;!-- app/view/news/list.tpl --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">html</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">head</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">title</span>&gt;</span>Egg HackerNews Clone<span class="tag">&lt;/<span class="name">title</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">"stylesheet"</span> <span class="attr">href</span>=<span class="string">"/public/css/news.css"</span> /&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">head</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">body</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">ul</span> <span class="attr">class</span>=<span class="string">"news-view view"</span>&gt;</span></span><br><span class="line">      &#123;% for item in list %&#125;</span><br><span class="line">        <span class="tag">&lt;<span class="name">li</span> <span class="attr">class</span>=<span class="string">"item"</span>&gt;</span></span><br><span class="line">          <span class="tag">&lt;<span class="name">a</span> <span class="attr">href</span>=<span class="string">"&#123;&#123; item.url &#125;&#125;"</span>&gt;</span>&#123;&#123; item.title &#125;&#125;<span class="tag">&lt;/<span class="name">a</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">li</span>&gt;</span></span><br><span class="line">      &#123;% endfor %&#125;</span><br><span class="line">    <span class="tag">&lt;/<span class="name">ul</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">html</span>&gt;</span></span><br></pre></td></tr></table></figure>
<p>Then add a controller and router.</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// app/controller/news.js</span></span><br><span class="line"><span class="keyword">const</span> Controller = <span class="built_in">require</span>(<span class="string">'egg'</span>).Controller;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">NewsController</span> <span class="keyword">extends</span> <span class="title">Controller</span> </span>&#123;</span><br><span class="line">  <span class="keyword">async</span> list() &#123;</span><br><span class="line">    <span class="keyword">const</span> dataList = &#123;</span><br><span class="line">      list: [</span><br><span class="line">        &#123; <span class="attr">id</span>: <span class="number">1</span>, <span class="attr">title</span>: <span class="string">'this is news 1'</span>, <span class="attr">url</span>: <span class="string">'/news/1'</span> &#125;,</span><br><span class="line">        &#123; <span class="attr">id</span>: <span class="number">2</span>, <span class="attr">title</span>: <span class="string">'this is news 2'</span>, <span class="attr">url</span>: <span class="string">'/news/2'</span> &#125;</span><br><span class="line">      ]</span><br><span class="line">    &#125;;</span><br><span class="line">    <span class="keyword">await</span> <span class="keyword">this</span>.ctx.render(<span class="string">'news/list.tpl'</span>, dataList);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = NewsController;</span><br><span class="line"></span><br><span class="line"><span class="comment">// app/router.js</span></span><br><span class="line"><span class="built_in">module</span>.exports = <span class="function"><span class="params">app</span> =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">const</span> &#123; router, controller &#125; = app;</span><br><span class="line">  router.get(<span class="string">'/'</span>, controller.home.index);</span><br><span class="line">  router.get(<span class="string">'/news'</span>, controller.news.list);</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<p>Open a browser window and navigate to http://localhost:7001/news.
You should be able to see the rendered page.</p>
<p><strong>Tip：In development, Egg enables the <a href="https://github.com/eggjs/egg-development" target="_blank" rel="noopener">development</a> plug-in by default, which reloads your worker process when changes are made to your back-end code.</strong></p>
<h3 id="create-a-service"><a class="markdown-anchor" href="#create-a-service">#</a> Create a Service</h3>
<p>In practice, controllers usually won't generate data on their own,
neither will they contain complicated business logic.
Complicated business logic should be abstracted as
a busineess logic layer instead, i.e., <a href="../basics/service.html">service</a>.</p>
<p>Let's create a service to fetch data from the
<a href="https://github.com/HackerNews/API" target="_blank" rel="noopener">HackerNews</a>.</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// app/service/news.js</span></span><br><span class="line"><span class="keyword">const</span> Service = <span class="built_in">require</span>(<span class="string">'egg'</span>).Service;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">NewsService</span> <span class="keyword">extends</span> <span class="title">Service</span> </span>&#123;</span><br><span class="line">  <span class="keyword">async</span> list(page = <span class="number">1</span>) &#123;</span><br><span class="line">    <span class="comment">// read config</span></span><br><span class="line">    <span class="keyword">const</span> &#123; serverUrl, pageSize &#125; = <span class="keyword">this</span>.config.news;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// use build-in http client to GET hacker-news api</span></span><br><span class="line">    <span class="keyword">const</span> &#123; <span class="attr">data</span>: idList &#125; = <span class="keyword">await</span> <span class="keyword">this</span>.ctx.curl(<span class="string">`<span class="subst">$&#123;serverUrl&#125;</span>/topstories.json`</span>, &#123;</span><br><span class="line">      data: &#123;</span><br><span class="line">        orderBy: <span class="string">'"$key"'</span>,</span><br><span class="line">        startAt: <span class="string">`"<span class="subst">$&#123;pageSize * (page - <span class="number">1</span>)&#125;</span>"`</span>,</span><br><span class="line">        endAt: <span class="string">`"<span class="subst">$&#123;pageSize * page - <span class="number">1</span>&#125;</span>"`</span>,</span><br><span class="line">      &#125;,</span><br><span class="line">      dataType: <span class="string">'json'</span>,</span><br><span class="line">    &#125;);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// parallel GET detail</span></span><br><span class="line">    <span class="keyword">const</span> newsList = <span class="keyword">await</span> <span class="built_in">Promise</span>.all(</span><br><span class="line">      <span class="built_in">Object</span>.keys(idList).map(<span class="function"><span class="params">key</span> =&gt;</span> &#123;</span><br><span class="line">        <span class="keyword">const</span> url = <span class="string">`<span class="subst">$&#123;serverUrl&#125;</span>/item/<span class="subst">$&#123;idList[key]&#125;</span>.json`</span>;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span>.ctx.curl(url, &#123; <span class="attr">dataType</span>: <span class="string">'json'</span> &#125;);</span><br><span class="line">      &#125;)</span><br><span class="line">    );</span><br><span class="line">    <span class="keyword">return</span> newsList.map(<span class="function"><span class="params">res</span> =&gt;</span> res.data);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = NewsService;</span><br></pre></td></tr></table></figure>
<blockquote>
<p>Egg has <a href="../core/httpclient.html">HttpClient</a> built in in order to help you make HTTP requests.</p>
</blockquote>
<p>Then slightly modify our previous controller.</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// app/controller/news.js</span></span><br><span class="line"><span class="keyword">const</span> Controller = <span class="built_in">require</span>(<span class="string">'egg'</span>).Controller;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">NewsController</span> <span class="keyword">extends</span> <span class="title">Controller</span> </span>&#123;</span><br><span class="line">  <span class="keyword">async</span> list() &#123;</span><br><span class="line">    <span class="keyword">const</span> ctx = <span class="keyword">this</span>.ctx;</span><br><span class="line">    <span class="keyword">const</span> page = ctx.query.page || <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">const</span> newsList = <span class="keyword">await</span> ctx.service.news.list(page);</span><br><span class="line">    <span class="keyword">await</span> ctx.render(<span class="string">'news/list.tpl'</span>, &#123; <span class="attr">list</span>: newsList &#125;);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = NewsController;</span><br></pre></td></tr></table></figure>
<p>And also add config.</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// config/config.default.js</span></span><br><span class="line"><span class="comment">// add news' configurations</span></span><br><span class="line">exports.news = &#123;</span><br><span class="line">  pageSize: <span class="number">5</span>,</span><br><span class="line">  serverUrl: <span class="string">'https://hacker-news.firebaseio.com/v0'</span>,</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<h3 id="add-extensions"><a class="markdown-anchor" href="#add-extensions">#</a> Add Extensions</h3>
<p>We might encounter a small problem here.
The time that we fetched are Unix Time format,
whereas we want to present them in a more friendly way to read.</p>
<p>Egg provides us with a quick way to extend its functionalities.
We just need to add extension scripts to the <code>app/extend</code> directory.
For more information, cf. <a href="../basics/extend.html">Extensions</a>.</p>
<p>In the case of view, we can just write a helper as an extension.</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ npm i moment --save</span><br></pre></td></tr></table></figure>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// app/extend/helper.js</span></span><br><span class="line"><span class="keyword">const</span> moment = <span class="built_in">require</span>(<span class="string">'moment'</span>);</span><br><span class="line">exports.relativeTime = <span class="function"><span class="params">time</span> =&gt;</span> moment(<span class="keyword">new</span> <span class="built_in">Date</span>(time * <span class="number">1000</span>)).fromNow();</span><br></pre></td></tr></table></figure>
<p>Then use it in the templates.</p>
<figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="comment">&lt;!-- app/view/news/list.tpl --&gt;</span></span><br><span class="line">&#123;&#123; helper.relativeTime(item.time) &#125;&#125;</span><br></pre></td></tr></table></figure>
<h3 id="add-middlewares"><a class="markdown-anchor" href="#add-middlewares">#</a> Add Middlewares</h3>
<p>Suppose that we wanted to prohibit accesses from Baidu crawlers.</p>
<p>Smart developers might quickly guess that we can achieve it by adding a <a href="../basics/middleware.html">middleware</a>
that checks the User-Agent.</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// app/middleware/robot.js</span></span><br><span class="line"><span class="comment">// options === app.config.robot</span></span><br><span class="line"><span class="built_in">module</span>.exports = <span class="function">(<span class="params">options, app</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">async</span> <span class="function"><span class="keyword">function</span> <span class="title">robotMiddleware</span>(<span class="params">ctx, next</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">const</span> source = ctx.get(<span class="string">'user-agent'</span>) || <span class="string">''</span>;</span><br><span class="line">    <span class="keyword">const</span> match = options.ua.some(<span class="function"><span class="params">ua</span> =&gt;</span> ua.test(source));</span><br><span class="line">    <span class="keyword">if</span> (match) &#123;</span><br><span class="line">      ctx.status = <span class="number">403</span>;</span><br><span class="line">      ctx.message = <span class="string">'Go away, robot.'</span>;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      <span class="keyword">await</span> next();</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// config/config.default.js</span></span><br><span class="line"><span class="comment">// add middleware robot</span></span><br><span class="line">exports.middleware = [</span><br><span class="line">  <span class="string">'robot'</span></span><br><span class="line">];</span><br><span class="line"><span class="comment">// robot's configurations</span></span><br><span class="line">exports.robot = &#123;</span><br><span class="line">  ua: [</span><br><span class="line">    /Baiduspider/i,</span><br><span class="line">  ]</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<p>Now try it using <code>curl localhost:7001/news -A &quot;Baiduspider&quot;</code>.</p>
<p>See <a href="../basics/middleware.html">Middleware</a> for more details.</p>
<h3 id="add-configurations"><a class="markdown-anchor" href="#add-configurations">#</a> Add Configurations</h3>
<p>When writing business logic,
it is inevitable that we need to manage configurations.
Egg provides a powerful way to manage them in a merged configuration file.</p>
<ul>
<li>Environment-specific configuration files are well supported, e.g. config.local.js, config.prod.js, etc.</li>
<li>Configurations could be set wherever convenient, e.g. near Applications/Plug-ins/Framesworks, and Egg will be careful to merge and load them.</li>
<li>For more information on merging, see <a href="../basics/config.html">Configurations</a>.</li>
</ul>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// config/config.default.js</span></span><br><span class="line">exports.robot = &#123;</span><br><span class="line">  ua: [</span><br><span class="line">    /curl/i,</span><br><span class="line">    /Baiduspider/i,</span><br><span class="line">  ],</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// config/config.local.js</span></span><br><span class="line"><span class="comment">// only read at development mode, will override default</span></span><br><span class="line">exports.robot = &#123;</span><br><span class="line">  ua: [</span><br><span class="line">    /Baiduspider/i,</span><br><span class="line">  ],</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// app/service/some.js</span></span><br><span class="line"><span class="keyword">const</span> Service = <span class="built_in">require</span>(<span class="string">'egg'</span>).Service;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">SomeService</span> <span class="keyword">extends</span> <span class="title">Service</span> </span>&#123;</span><br><span class="line">  <span class="keyword">async</span> list() &#123;</span><br><span class="line">    <span class="keyword">const</span> rule = <span class="keyword">this</span>.config.robot.ua;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = SomeService;</span><br></pre></td></tr></table></figure>
<h3 id="add-unit-testing"><a class="markdown-anchor" href="#add-unit-testing">#</a> Add Unit Testing</h3>
<p>Unit Testing is very important, and Egg also provides <a href="https://github.com/eggjs/egg-bin" target="_blank" rel="noopener">egg-bin</a> to help you write tests painless.</p>
<p>All the test files should be placed at <code>{app_root}/test/**/*.test.js</code>.</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// test/app/middleware/robot.test.js</span></span><br><span class="line"><span class="keyword">const</span> &#123; app, mock, assert &#125; = <span class="built_in">require</span>(<span class="string">'egg-mock/bootstrap'</span>);</span><br><span class="line"></span><br><span class="line">describe(<span class="string">'test/app/middleware/robot.test.js'</span>, () =&gt; &#123;</span><br><span class="line">  it(<span class="string">'should block robot'</span>, () =&gt; &#123;</span><br><span class="line">    <span class="keyword">return</span> app.httpRequest()</span><br><span class="line">      .get(<span class="string">'/'</span>)</span><br><span class="line">      .set(<span class="string">'User-Agent'</span>, <span class="string">"Baiduspider"</span>)</span><br><span class="line">      .expect(<span class="number">403</span>);</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<p>Then add <code>npm scripts</code>.</p>
<figure class="highlight json"><table><tr><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="attr">"scripts"</span>: &#123;</span><br><span class="line">    <span class="attr">"test"</span>: <span class="string">"egg-bin test"</span>,</span><br><span class="line">    <span class="attr">"cov"</span>: <span class="string">"egg-bin cov"</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>Also install dependencies.</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ npm i egg-mock --save-dev</span><br></pre></td></tr></table></figure>
<p>Run it.</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ npm <span class="built_in">test</span></span><br></pre></td></tr></table></figure>
<p>That is all of it, for more detail, see <a href="../core/unittest.html">Unit Testing</a>.</p>
<h2 id="conclusions"><a class="markdown-anchor" href="#conclusions">#</a> Conclusions</h2>
<p>We can only touch the tip of the iceberg of Egg with the above short sections.
Where to go from here? read our documentation to better understand the framework.</p>
<ul>
<li>About Egg boilerplate type, See <a href="../tutorials/index.html">Boilerplate Type Description</a>.</li>
<li>Egg provides a powerful mechanism for extending features. See <a href="../basics/plugin.html">Plugin</a>.</li>
<li>Egg framework allows small or large teams to work together as fast as possible under the well-documented conventions and coding best practices. In addition, the teams can build up logics on top of the framework to better suit their special needs. See more on [Frameworks].(../advanced/framework.md).</li>
<li>Egg framework provides code reusabilities and modularities. See details at <a href="../tutorials/progressive.html">Progressive</a>.</li>
<li>Egg framework enables developers to write painless unit testing with many plugins and community-powered toolings. The team should give it a try by using Egg unit testing without worrying about setting up the testing tooling but writing the testing logics. See <a href="../core/unittest.html">Unit Testing</a>.</li>
</ul>

  </article>
  <aside id="mobileAside" class="toc">
  <div class="mobile-menu">
    <ul>
      <li><a href="/en/intro/" alt="Guide">Guide</a></li><li><a href="/api/" alt="API">API</a></li><li><a href="/en/tutorials/index.html" alt="Tutorials">Tutorials</a></li><li><a href="https://github.com/search?q=topic%3Aegg-plugin&type=Repositories" alt="Plugins">Plugins</a></li><li><a href="https://github.com/eggjs/egg/releases" alt="Release">Release</a></li>
      
      
        <li class="translations">
          <a class="nav-link">中文文档</a>
          <span class="arrow"></span><ul id="dropdownContent" class="dropdown-content"><li><a id="en" href="/en/intro/quickstart.html" style="color: #22ab28">English</a></li><li><a id="zh-cn" href="/zh-cn/intro/quickstart.html" >中文</a></li></ul>
        </li>
      
    </ul>
  </div>
  <dl><dt id="title-Intro" style="cursor: pointer;" class="aside-title">Guide<a id="collapse-icon-Intro" class="icon opend"></a></dt><dd id=panel-Intro><ul><li><a href="/en/intro/index.html" class="menu-link">What is Egg?</a></li><li><a href="/en/intro/egg-and-koa.html" class="menu-link">Egg and Koa</a></li><li><a href="/en/intro/quickstart.html" class="menu-link">Quick Start</a></li><li><a href="/en/tutorials/progressive.html" class="menu-link">Progressive</a></li><li><a href="/en/migration.html" class="menu-link">Migration to 2.x</a></li></ul></dd><dt id="title-Basics" style="cursor: pointer;" class="aside-title">Basis Function<a id="collapse-icon-Basics" class="icon opend"></a></dt><dd id=panel-Basics><ul><li><a href="/en/basics/structure.html" class="menu-link">Directory Structure</a></li><li><a href="/en/basics/objects.html" class="menu-link">Built-in Objects</a></li><li><a href="/en/basics/env.html" class="menu-link">Environment</a></li><li><a href="/en/basics/config.html" class="menu-link">Configuration</a></li><li><a href="/en/basics/middleware.html" class="menu-link">Middleware</a></li><li><a href="/en/basics/router.html" class="menu-link">Router</a></li><li><a href="/en/basics/controller.html" class="menu-link">Controller</a></li><li><a href="/en/basics/service.html" class="menu-link">Service</a></li><li><a href="/en/basics/plugin.html" class="menu-link">Plugin</a></li><li><a href="/en/basics/schedule.html" class="menu-link">Schedule</a></li><li><a href="/en/basics/extend.html" class="menu-link">Extend</a></li><li><a href="/en/basics/app-start.html" class="menu-link">Custom Init</a></li></ul></dd><dt id="title-Core" style="cursor: pointer;" class="aside-title">Core<a id="collapse-icon-Core" class="icon opend"></a></dt><dd id=panel-Core><ul><li><a href="/en/core/development.html" class="menu-link">Development</a></li><li><a href="/en/core/unittest.html" class="menu-link">Unit Testing</a></li><li><a href="/en/core/deployment.html" class="menu-link">Deployment</a></li><li><a href="/en/core/logger.html" class="menu-link">Logger</a></li><li><a href="/en/core/httpclient.html" class="menu-link">HttpClient</a></li><li><a href="/en/core/cookie-and-session.html" class="menu-link">Cookie and Session</a></li><li><a href="/en/core/cluster-and-ipc.html" class="menu-link">Cluster and IPC</a></li><li><a href="/en/core/view.html" class="menu-link">View</a></li><li><a href="/en/core/error-handling.html" class="menu-link">Error Handling</a></li><li><a href="/en/core/security.html" class="menu-link">Security</a></li><li><a href="/en/core/i18n.html" class="menu-link">i18n</a></li></ul></dd><dt id="title-Tutorials" style="cursor: pointer;" class="aside-title">Tutorials<a id="collapse-icon-Tutorials" class="icon opend"></a></dt><dd id=panel-Tutorials><ul><li><a href="/en/tutorials/mysql.html" class="menu-link">MySQL</a></li><li><a href="/en/tutorials/restful.html" class="menu-link">RESTful API</a></li><li><a href="/en/tutorials/passport.html" class="menu-link">Passport</a></li><li><a href="/en/tutorials/socketio.html" class="menu-link">Socket.IO</a></li><li><a href="/en/tutorials/assets.html" class="menu-link">Assets</a></li><li><a href="/en/tutorials/typescript.html" class="menu-link">TypeScript</a></li></ul></dd><dt id="title-Advanced" style="cursor: pointer;" class="aside-title">Advanced<a id="collapse-icon-Advanced" class="icon opend"></a></dt><dd id=panel-Advanced><ul><li><a href="/en/advanced/loader.html" class="menu-link">Loader</a></li><li><a href="/en/advanced/plugin.html" class="menu-link">Plugin Development</a></li><li><a href="/en/advanced/framework.html" class="menu-link">Framework</a></li><li><a href="/en/advanced/cluster-client.html" class="menu-link">Cluster Enhancement</a></li><li><a href="/en/advanced/view-plugin.html" class="menu-link">View Plugin</a></li><li><a href="/en/style-guide.html" class="menu-link">Style Guide</a></li></ul></dd><dt id="title-Community" style="cursor: pointer;" class="aside-title">Community<a id="collapse-icon-Community" class="icon opend"></a></dt><dd id=panel-Community><ul><li><a href="/en/plugins/" class="menu-link">Plugin List</a></li><li><a href="/en/contributing.html" class="menu-link">Contributing</a></li><li><a href="/en/resource.html" class="menu-link">Resource</a></li><li><a href="/en/faq.html" class="menu-link">FAQ</a></li></ul></dd></dl>
</aside>
<script>
var mobileTrigger = document.getElementById('mobileTrigger');
var mobileAside = document.getElementById('mobileAside');

var expandMenu = function(title) {
  // handle icon
  const collapseIcon = document.getElementById('collapse-icon-' + title);
  if (collapseIcon) {
    collapseIcon.className = 'icon opend';
  }
  // handle panelEle
  const panelEle = document.getElementById('panel-' + title);
  if (panelEle) {
    panelEle.className = '';
  }
}

var collapseMenu = function(title) {
  // handle icon
  const collapseIcon = document.getElementById('collapse-icon-' + title);
  if (collapseIcon) {
    collapseIcon.className = 'icon closed';
  }
  // handle panelEle
  const panelEle = document.getElementById('panel-' + title);
  if (panelEle) {
    panelEle.className = 'aside-panel-hidden';
  }
}

mobileAside.onclick = function(e) {
  const targetId = e.target.id;
  if (targetId && (targetId.indexOf('title-') > -1 || targetId.indexOf('collapse-icon-') > -1)) {
    const title = targetId.replace('title-', '').replace('collapse-icon-', '');
    try { 
      // the the browser may have no localStroage or JSON.parse may throw exception.
      const menuInfo = JSON.parse(window.localStorage.getItem('menuInfo'));
        
      // current menu status
      const curClosed = menuInfo[title] ? menuInfo[title].closed : false; // default false

      // change UI
      curClosed ? expandMenu(title) : collapseMenu(title);

      // save menuInfo to localStorage
      menuInfo[title] = { closed: !curClosed } // opposite
      window.localStorage.setItem('menuInfo', JSON.stringify(menuInfo));
    } catch (e) {}
  }
};

mobileTrigger.onclick = function(e) {
  e.preventDefault();
  if (mobileAside.className.indexOf('mobile-show') === -1) {
    mobileAside.className += ' mobile-show';
  } else {
    mobileAside.className = 'toc';
  }
};

(function() {
  // save data to localStorage because the page will refresh when user change the url.
  let menuInfo;
  try { 
    // the the browser may have no localStroage or JSON.parse may throw exception.
    menuInfo = JSON.parse(window.localStorage.getItem('menuInfo'));
    if (!menuInfo) {
      menuInfo = {};
      window.localStorage.setItem('menuInfo', JSON.stringify(menuInfo));
    }
  } catch (e) {
    menuInfo = {}; // default {}
  }

  for (const title in menuInfo) {
    if (menuInfo[title] && menuInfo[title].closed) { // menu in closed status.
      collapseMenu(title);
    } else {
      expandMenu(title);
    }
  }

  // highlight menu
  const pathname = window.location.pathname;
  const selector = `a[href="${pathname}"].menu-link,a[href="${pathname}index.html"].menu-link`;
  const menuItem = mobileAside.querySelector(selector);
  if (menuItem) { menuItem.className += ' highlight'; }
})();
</script>

</div>

  </div>
</body>
<script src="https://cdn.jsdelivr.net/docsearch.js/2/docsearch.min.js"></script>
<script>
docsearch({
  apiKey: '1561de31a86f79507ea00cdb54ce647c',
  indexName: 'eggjs',
  inputSelector: '#search-query',
});
</script>
<div class="cnzz">
<script src="https://s11.cnzz.com/z_stat.php?id=1261142226&web_id=1261142226" language="JavaScript"></script>
</div>

</html>
